<template>
  <el-form class="pro-panel" ref="formRef" :model="formData" v-bind="formProps" @submit.prevent>
    <FormGroup :group="group" :form-data="formData" v-for="group in groups" :key="group.key">
      <el-row>
        <el-col
          :span="column.span"
          :offset="column.offset"
          v-for="column in group.columns?.filter((column) => column.visible !== false)"
          :key="column.prop"
          v-bind="column.colProps"
        >
          <el-form-item
            :class="column.className"
            :label="column.label"
            :prop="column.prop"
            :rules="getRules(column)"
          >
            <template #label v-if="/^\s+$/.test(column.label)">&nbsp;</template>
            <!-- option 组件 -->
            <component
              :is="components[column.type]"
              v-if="components[column.type] && optionComponents[column.type]"
              v-bind="column.formProps"
              :model-value="getValue(column.prop)"
              @update:model-value="(value: any) => setValue(column.prop, value)"
              @change="handleUpdate"
            >
              <component
                :is="optionComponents[column.type]"
                v-for="option in getOptions(column)"
                :key="option.value"
                :value="option.value"
                :label="column.type === 'select' ? option.label : option.value"
              >
                {{ option.label }}
              </component>
            </component>

            <!-- 普通组件 -->
            <component
              :is="components[column.type]"
              v-else-if="components[column.type]"
              v-bind="column.formProps"
              :model-value="getValue(column.prop)"
              @update:model-value="(value: any) => setValue(column.prop, value)"
              @change="handleUpdate"
            />

            <!-- 纯文本组件 -->
            <div v-else-if="column.type === 'text'" :props="column.formProps">
              {{ getValue(column.prop) }}
            </div>

            <!-- 自定义 slot -->
            <slot
              v-else-if="column.type === 'slot'"
              :name="column.prop"
              :props="column.formProps"
              :row="column"
            />
            <div v-if="column.formProps?.unit" class="unit" :class="column.formProps?.unitPosition">
              {{ column.formProps?.unit }}
            </div>
          </el-form-item>
        </el-col>
      </el-row>
    </FormGroup>
  </el-form>
</template>

<script setup lang="ts">
import {
  ElCascader,
  ElCheckbox,
  ElCheckboxGroup,
  ElColorPicker,
  ElDatePicker,
  ElForm,
  ElFormItem,
  ElInput,
  ElInputNumber,
  ElOption,
  ElRadio,
  ElRadioGroup,
  ElSelect,
  ElSlider,
  ElSwitch,
  ElTimePicker,
  ElTransfer,
  ElTreeSelect,
} from 'element-plus';
import { CSSProperties, computed, ref, watch } from 'vue';
import type { Column, Group, Option, TypeEnum } from './index.typing';
import { enumToOptions } from './hooks';
import FormGroup from './components/form-group/index.vue';
import ProgressInput from './components/progress-input/index.vue';
import ImageSelector from './components/image-selector/index.vue';
import ImageSelector1 from './components/image-selector1/index.vue';
import Tab from './components/tab/index.vue';

interface IProps {
  rules?: Partial<Record<string, any>> | undefined;
  labelPosition?: 'left' | 'right' | 'top';
  requireAsteriskPosition?: 'left' | 'right';
  labelWidth?: string | number;
  labelSuffix?: string;
  inline?: boolean;
  inlineMessage?: boolean;
  statusIcon?: boolean;
  showMessage?: boolean;
  size?: '' | 'default' | 'small' | 'large';
  disabled?: boolean;
  validateOnRuleChange?: boolean;
  hideRequiredAsterisk?: boolean;
  scrollToError?: boolean;
  style?: CSSProperties;

  columns?: Column[];
  data?: Record<string, any>;
  groups?: Group[];
}

defineOptions({
  name: 'ProPanel',
});

const props = withDefaults(defineProps<IProps>(), {
  labelPosition: 'top',
  labelWidth: 160,
  showMessage: true,
  validateOnRuleChange: true,
  group: () => [],
});

const emits = defineEmits(['update:data']);

const formRef = ref<InstanceType<typeof ElForm>>();
const formData = ref(props.data || {});
const formProps = computed(() => {
  const { labelWidth, labelPosition, ...restProps } = props;
  return {
    ...restProps,
    labelPosition,
    labelWidth,
    style: {
      '--item-content-width': '400px',
      ...restProps.style,
    },
  };
});

// 递归获取嵌套路径的值
const getValue = (prop: string) => {
  const keys = prop.split('.');
  return keys.reduce(
    (current, key) => (typeof current?.[key] === 'undefined' ? undefined : current?.[key]),
    formData.value
  );
};

// 递归设置嵌套路径的值
const setValue = (prop: string, value: any) => {
  const data = formData.value;
  const keys = prop.split('.');
  const lastKey = keys.pop();
  if (lastKey) {
    const parent = keys.reduce(
      (current, key) =>
        typeof current?.[key] === 'undefined' ? (current[key] = {}) : current?.[key],
      data
    );
    if (parent) {
      parent[lastKey] = value;
    }
  }
};

watch(
  () => props.data,
  () => {
    formData.value = props.data || {};
  }
);

const handleUpdate = () => {
  emits('update:data', formData.value);
};

const components: Partial<Record<TypeEnum, any>> = {
  hidden: ElInput,
  input: ElInput,
  'input-number': ElInputNumber,
  switch: ElSwitch,
  slider: ElSlider,
  cascader: ElCascader,
  transfer: ElTransfer,
  select: ElSelect,
  radio: ElRadioGroup,
  checkbox: ElCheckboxGroup,
  'date-picker': ElDatePicker,
  'time-picker': ElTimePicker,
  'color-picker': ElColorPicker,
  'tree-select': ElTreeSelect,
  'progress-input': ProgressInput,
  'image-selector': ImageSelector,
  'image-selector1': ImageSelector1,
  tab: Tab,
};

const optionComponents: Partial<Record<TypeEnum, any>> = {
  select: ElOption,
  radio: ElRadio,
  checkbox: ElCheckbox,
};

const getRules = (column: Column) => {
  let result;
  if (column.required) {
    result = [{ required: true, message: '不能为空' }];
  } else {
    if (Array.isArray(column.rules)) {
      result = column.rules.map((rule) => ({
        ...rule,
        message: rule.required && !rule.message ? '不能为空' : rule.message,
      }));
    }
  }

  return result;
};

const getOptions = (column: Column) => {
  let result = enumToOptions(column.options);
  if (column.formProps?.withAll) {
    const optionsAll: Option[] = [{ value: '', label: '全部' }];
    result = optionsAll.concat(result);
  }

  return result;
};

defineExpose({
  get form() {
    return formRef.value;
  },
  validate: async () => {
    let values: Record<string, any> = {};
    let error;
    try {
      const validateStatus = await formRef.value?.validate();
      if (validateStatus) {
        values = formData.value;
      }
    } catch (err) {
      error = err;
    }
    return { values, error };
  },
});
</script>

<style lang="less" scoped>
.pro-panel {
  :deep(.el-form-item) {
    // flex-direction: column;
    &.labelPositionLeft,
    &.labelPositionRight {
      display: flex;
      .el-form-item__label {
        margin-bottom: 0;
      }
      .el-form-item__content {
        flex: 1;
        justify-content: end;
        line-height: auto;
      }
    }
    &.labelPositionLeft {
      .el-form-item__label {
        text-align: left;
      }
    }
    &.labelPositionRight {
      .el-form-item__label {
        text-align: right;
      }
    }
    .el-form-item__label {
      justify-content: flex-start;
    }
    .el-form-item__content {
      max-width: var(--item-content-width);
      .unit {
        position: absolute;
        right: 8px;
        font-size: 12px;
        font-family: PingFangSC, PingFang SC;
        font-weight: 400;
        color: #ffffff;
        &.left {
          left: 8px;
        }
        &.right {
          right: 8px;
        }
      }
    }
    .el-select,
    .el-input-number,
    .el-cascader,
    .el-date-editor {
      width: 100%;
    }
    .el-tree-node__content {
      height: 32px;
    }
  }
}
</style>
